import {scan} from './LexParser.js'

let syntax = {
    Program : [["Statement","EOF"]],
    StatementList : [
        ["Statement"],
        ["StatementList","Statement"],
    ],
    Statement:[
        ["ExpressionStatement"],
        ["IfStatement"],
        ["WhileStatement"],
        ["Block"],
        ["BreakStatement"],
        ["ContinueStatement"],
        ["VariableDeclaration"],
        ["FunctionDeclaration"],
    ],
    IfStatement:[
        ["if","(","Expression",")","Statement"]
    ],
    BreakStatement:[
        ["break",';']
    ],
    ContinueStatement:[
        ["continue",';']
    ],
    WhileStatement:[
        ["while","(","Expression",")","Statement"]
    ],
    Block:[
        ["{","StatementList","}"],
        ["{","}"]
    ],
    VariableDeclaration:[
        ["var","Identifier",";"],
        ["let","Identifier",";"]
    ],
    FunctionDeclaration:[
        ["function","Identifier","(",")","{","StatementList","}"]
    ],
    ExpressionStatement:[
        ["Expression",";"]
    ],
    Expression:[
        ["AssignmentExpression"]
    ],
    AssignmentExpression:[
        ["LeftHandSideExpression","=","LogicalORExpression"],
        ["LogicalORExpression"],
    ],
    LogicalORExpression:[
        ["LogicalANDExpression"],
        ["LogicalORExpression","||","LogicalANDExpression"],
    ],
    LogicalANDExpression:[
        ["AdditiveExpression"],
        ["LogicalANDExpression","||","AdditiveExpression"],
    ],
    AdditiveExpression:[
        ["MultiplicativeExpression"],
        ["AdditiveExpression","+","MultiplicativeExpression"],
        ["AdditiveExpression","-","MultiplicativeExpression"]
    ],
    MultiplicativeExpression:[
        ["PrimaryExpression"],
        ["MultiplicativeExpression","*","PrimaryExpression"],
        ["MultiplicativeExpression","/","PrimaryExpression"]
    ],
    LeftHandSideExpression:[
        ["CallExpression"],
        ["NewExpression"]
    ],
    CallExpression:[
        ["MemberExpression","Arguments"],
        ["CallExpression","Arguments"]
    ],
    Arguments:[
        ["(",")"],
        ["(","ArgumentList",")"],
    ],
    ArgumentList:[
        ["AssignmentExpression"],
        ["ArgumentList",",","AssignmentExpression"],
    ],
    NewExpression:[
        ["MemberExpression"],
        ["new","NewExpression"],
    ],
    MemberExpression:[
        ["PrimaryExpression"],
        ["PrimaryExpression",".","Identifier"],
        ["PrimaryExpression","[","Expression","]"],
    ],
    PrimaryExpression:[
        ["(","Expression",")"],
        ["Literal"],
        ["Identifier"]
    ],
    Literal:[
        ["NumericLiteral"],
        ["StringLiteral"],
        ["BooleanLiteral"],
        ["NullLiteral"],
        ["RegularExpressionLiteral"],
        ["ObjectLiteral"],
        ["ArrayLiteral"],
    ],
    ObjectLiteral:[
        ["{","}"],
        ["{","PropertyList","}"],
    ],
    PropertyList:[
        ["Property"],
        ["PropertyList",",","Property"]
    ],
    Property:[
        ["StringLiteral",":","AdditiveExpression"],
        ["Identifier",":","AdditiveExpression"]
    ],
}

let hash ={

}

function closure(state){
    hash[JSON.stringify(state)] = state;
    let queue = [];
    for (const symbol in state) {
       if(symbol.match(/^\$/)){
           continue;
       }
       queue.push(symbol);
    }
    while (queue.length) {
        let symbol = queue.shift();
        // console.log(symbol);
        if (syntax[symbol]) {
            for (const rule of syntax[symbol]) {
                if(!state[rule[0]]){
                    queue.push(rule[0])
                }
                let current = state;
                for (const part of rule) {
                    if (!current[part]) {
                        current[part] = {}
                    }
                    current = current[part]
                }
                current.$reduceType = symbol;
                current.$reduceLength = rule.length;
            }
        }
    }
    for (const symbol in state) {
        if(symbol.match(/^\$/)){
            continue;
        }
        if (hash[JSON.stringify(state[symbol])]) {
            state[symbol] = hash[JSON.stringify(state[symbol])];
        }else{
           closure(state[symbol]); 
        }
        
     }
}

let end = {
    $isEnd:true
}

let start = {
    "Program":end
}

closure(start);

let source = (`
var a;
`)

export function parse(source){
    let stack = [start];
    function reduce(){
        let state = stack[stack.length -1];
        if(state.$reduceType){
            let children = [];
            for (let i = 0; i < state.$reduceLength; i++) {
                children.push(stack.pop());
            }

            return{
                type:state.$reduceType,
                children:children.reverse()
            };
        }else{
            throw new Error("unexpected token");
        }
    }
    function shift(symbol){
        let state = stack[stack.length -1];
        if(symbol.type in state){
            stack.push(state[symbol.type])
        }else{
            shift(reduce());
            shift(symbol);
        }
    }
    for (const symbol of scan(source)) {
        shift(symbol);
    }
    return reduce();
}

// console.log(parse(source));
